package znet

import (
	"errors"
	"fmt"
	"io"
	"net"
	"sync"
	"zinx/utils"
	"zinx/ziface"
)

type Connection struct {
	//当前Conn隶属于哪个Server
	TcpServer ziface.IServer
	//当前连接对应的TCP套接字
	Conn *net.TCPConn
	//连接ID
	ConnID uint32
	//当前连接状态
	IsClosed bool
	//告知当前连接已经退出的Channel(Reader通知Writer)
	ExitChan chan bool
	//无缓冲的管道，用于读、写Goroutine之间的消息通信
	msgChan chan []byte
	//该连接处理的方法集合，不同msgId对应不同的API
	MsgHandler ziface.IMsgHandle
	//连接属性集合
	property map[string]interface{}
	//保护连接属性的锁
	propertyLock sync.RWMutex
}

// NewConnection 初始化连接的方法
func NewConnection(server ziface.IServer, conn *net.TCPConn, connID uint32, msgHandler ziface.IMsgHandle) *Connection {
	c := &Connection{
		TcpServer: server,
		Conn:      conn,
		ConnID:    connID,
		IsClosed:  false,
		//所有对Request的处理都封装在Router中
		MsgHandler: msgHandler,
		msgChan:    make(chan []byte),
		ExitChan:   make(chan bool),
		property:   make(map[string]interface{}),
	}
	//将conn加入连接管理器中
	c.TcpServer.GetConnMgr().Add(c)
	return c
}

func (c *Connection) StartReader() {
	fmt.Println("[Reader Goroutine is running]")
	defer c.Stop()
	defer fmt.Println("[Reader is exit], connID=", c.ConnID, " , remote attr is ", c.GetRemoteAddr().String())
	for {
		/*读取客户端的数据到buf中
		buf := make([]byte, utils.GlobalObject.MaxPackageSize)
		_, err := c.Conn.Read(buf)
		if err != nil {
			fmt.Println("receive buf err: ", err)
			continue
		}*/

		//创建一个拆包对象
		dp := NewDatapack()
		//读取客户端的MsgHead(二进制流)
		headData := make([]byte, dp.GetHeadLen())
		if _, err := io.ReadFull(c.GetTCPConnection(), headData); err != nil {
			fmt.Println("read msg head error: ", err)
			break
		}
		//拆包，得到msgId和msgDataLen，放到一个msg对象中
		msg, err := dp.Unpack(headData)
		if err != nil {
			fmt.Println("unpack error: ", err)
			break
		}
		//根据dataLen再次读取data，放在msg中
		var data []byte
		if msg.GetMsgLen() > 0 {
			data = make([]byte, msg.GetMsgLen())
			if _, err := io.ReadFull(c.GetTCPConnection(), data); err != nil {
				fmt.Println("read msg data err: ", err)
				break
			}
		}
		msg.SetData(data)
		//得到当前conn的请求
		req := Request{
			conn: c,
			msg:  msg,
		}
		//是否已经开启工作任务池
		if utils.GlobalObject.WorkerPoolSize > 0 {
			c.MsgHandler.SendMsgToTaskQueue(&req)
		} else {
			//根据绑定好的msgId找到对应的消息请求处理API(开一个goroutine)
			go c.MsgHandler.DoMsgHandler(&req)
		}
	}
}

// StartWriter 写消息的Goroutine，专门给客户端发送消息的模块
func (c *Connection) StartWriter() {
	fmt.Println("[Writer Goroutine is running]")
	defer fmt.Println("[conn Writer exit]", c.GetRemoteAddr().String())
	for {
		//阻塞等待channel消息
		select {
		case data := <-c.msgChan:
			if _, err := c.Conn.Write(data); err != nil {
				fmt.Println("Send data error: ", err)
				return
			}
		case <-c.ExitChan:
			//代表Reader已经退出，那么Writer也要退出
			return
		}
	}
}

func (c *Connection) Start() {
	fmt.Println("Conn Start...ConnId=", c.ConnID)
	//启动从当前连接读数据的业务
	go c.StartReader()
	//启动从当前连接写数据的业务
	go c.StartWriter()
	//调用创建连接之后的Hook函数
	c.TcpServer.CallOnConnCreate(c)
}

func (c *Connection) Stop() {
	fmt.Println("Conn Stop...ConnId=", c.ConnID)
	if c.IsClosed {
		return
	}
	c.IsClosed = true
	//调用销毁连接之前的Hook函数
	c.TcpServer.CallOnConnDestroy(c)
	//关闭socket连接
	c.Conn.Close()
	//告知writer关闭
	c.ExitChan <- true
	//将当前连接从ConnMgr中摘除
	c.TcpServer.GetConnMgr().Remove(c)
	//回收资源
	close(c.ExitChan)
	close(c.msgChan)
}

func (c *Connection) GetTCPConnection() *net.TCPConn {
	return c.Conn
}

func (c *Connection) GetConnID() uint32 {
	return c.ConnID
}

func (c *Connection) GetRemoteAddr() net.Addr {
	return c.Conn.RemoteAddr()
}

func (c *Connection) SendMsg(msgId uint32, data []byte) error {
	if c.IsClosed {
		return errors.New("connection closed when send msg")
	}
	//将data进行封包 MsgDataLen|MsgId|Data
	dp := NewDatapack()
	binaryMsg, err := dp.Pack(NewMessage(msgId, data))
	if err != nil {
		fmt.Println("pack msg err, id = ", msgId)
		return errors.New("pack msg err")
	}
	//将数据发送给管道
	c.msgChan <- binaryMsg
	return nil
}

func (c *Connection) SetProperty(key string, value interface{}) {
	c.propertyLock.Lock()
	defer c.propertyLock.Unlock()
	c.property[key] = value
}

func (c *Connection) GetProperty(key string) (interface{}, error) {
	c.propertyLock.RLock()
	defer c.propertyLock.RUnlock()
	if value, ok := c.property[key]; ok {
		return value, nil
	}
	return nil, errors.New("no property found")
}

func (c *Connection) RemoveProperty(key string) {
	c.propertyLock.Lock()
	defer c.propertyLock.Unlock()
	delete(c.property, key)
}
